﻿using System.Collections.Generic;
using System.Drawing;
using GeekShop.Commands;
using System.Drawing.Imaging;
using System.Windows.Forms;
using System.Drawing.Drawing2D;

namespace GeekShop
{
    public class Picture
    {
        #region Fields
        private bool pendingChanges = false;
        private string path;
        private Size size;
        private Mask mask = null;
        private PictureForm form;
        private Layer selectedLayer = null;
        private List<Layer> layers = new List<Layer>();
        private List<ICommand> undo = new List<ICommand>();
        private List<ICommand> redo = new List<ICommand>();
        #endregion

        #region Properties
        public string Path
        {
            get { return path; }
            set 
            { 
                this.path = value;
                this.form.Text = value;
            }
        }
        public Size Size
        {
            get { return size; }
            set { size = value; }
        }
        public Layer SelectedLayer
        {
            get { return selectedLayer; }
            set { selectedLayer = value; }
        }
        public Mask Mask
        {
            get
            {
                if (this.mask == null)
                {
                    this.mask = new Mask(new Bitmap(this.size.Width, this.size.Height));
                }
                return this.mask;
            }
            set { this.mask = value; }
        }
        internal List<Layer> Layers
        {
            get { return layers; }
            set { layers = value; }
        }
        public PictureForm Form
        {
            get { return form; }
            set { form = value; }
        }
        #endregion

        #region Virtual Properties
        public Bitmap Bitmap
        {
            get 
            {
                Bitmap bitmap = this.Preview;
                Graphics g = Graphics.FromImage(bitmap);

                if (this.selectedLayer.Visible)
                {
                    g.DrawImage(this.selectedLayer.Bitmap, new Rectangle(this.selectedLayer.Location, this.selectedLayer.Size));
                }

                if (this.mask != null)
                {
                    RectangleF bounds = this.mask.Region.GetBounds(g);
                    Rectangle rectangle = new Rectangle(
                        (int)bounds.Left,
                        (int)bounds.Top,
                        (int)bounds.Width,
                        (int)bounds.Height);
                    g.DrawRectangle(new Pen(new HatchBrush(HatchStyle.DarkUpwardDiagonal, Color.Black, Color.White)), rectangle);
                } 
                
                return bitmap; 
            }
        }
        public Bitmap Preview
        {
            get
            {
                Bitmap bitmap = new Bitmap(this.size.Width, this.size.Height);
                Graphics g = Graphics.FromImage(bitmap);

                g.FillRectangle(Picture.BackgroundBrush, new Rectangle(0, 0, this.size.Width, this.size.Height));

                foreach (Layer layer in this.layers)
                {
                    if (layer.Visible && layer != this.selectedLayer)
                    {
                        g.DrawImage(layer.Bitmap, new Rectangle(layer.Location, layer.Size));
                    }
                }

                return bitmap;
            }
        }
        #endregion

        #region Static Virtual Properties
        public static Brush BackgroundBrush
        {
            get
            {
                Size size = new Size(16, 16);
                int radiusX = (int)(size.Width / 2);
                int radiusY = (int)(size.Height / 2);

                Bitmap bitmap = new Bitmap(size.Width, size.Height);
                for (int x = -radiusX; x < radiusX; x++)
                {
                    for (int y = -radiusY; y < radiusY; y++)
                    {
                        bitmap.SetPixel(x + radiusX, y + radiusY, x * y > 0 ? Color.LightGray : Color.White);
                    }
                }
                return new TextureBrush(bitmap);
            }
        }
        #endregion

        #region Constructors
        public Picture(Layer bgLayer, Form mdiParent)
        {
            this.AddLayer(bgLayer);
            this.selectedLayer = bgLayer;
            this.size = bgLayer.Size;
            this.form = new PictureForm(this, mdiParent);
        }
        #endregion

        #region Public Methods
        public void AddLayer(Layer layer)
        {
            this.layers.Add(layer);
        }
        public void RemoveLayer(Layer layer)
        {
            this.layers.Remove(layer);
        }

        public void SaveBitmap()
        {
            this.Bitmap.Save(this.Path);
        }
        public void SaveBitmapAs(string path, ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters)
        {
            this.Path = path;
            this.Bitmap.Save(this.Path, imageCodecInfo, encoderParameters);
        }

        public void DoCommand(ICommand command) 
        {
            command.Do();
            this.undo.Add(command);
        }
        public void UndoCommand()
        {
            if (this.undo.Count > 0)
            {
                this.UndoCommand(this.undo[this.undo.Count - 1]);
            }
        }
        public void UndoCommand(ICommand command)
        {
            for (int i = this.undo.Count - 1; i >= 0; i--)
            {
                this.undo[i].Undo();
                this.undo.Remove(this.undo[i]);
                this.redo.Add(this.undo[i]);

                if (this.undo[i] == command)
                {
                    break;
                }
            }
        }
        public void RedoCommand()
        {
            if (this.redo.Count > 0)
            {
                this.RedoCommand(this.redo[this.redo.Count - 1]);
            }
        }
        public void RedoCommand(ICommand command)
        {
            for (int i = this.redo.Count - 1; i >= 0; i--)
            {
                this.redo[i].Undo();
                this.redo.Remove(this.redo[i]);
                this.undo.Add(this.redo[i]);

                if (this.redo[i] == command)
                {
                    break;
                }
            }
        }
        #endregion
    }
}
